#include "gameserver.h"
#include <iostream>
#include <algorithm>
#include <cmath>

#include "logmanager.h"
#include <sstream>


using namespace std;

GameServer::GameServer() :
	nextClientID(1),
	numClients(0),
	gameworld(true, this)
{
}

GameServer::~GameServer()
{
}

bool GameServer::Start(unsigned short port)
{
	isRunning = true;
	if (network.StartUDPListening(port))
	{
		Launch();
		return true;
	}
	return false;
}

void GameServer::Stop()
{
	isRunning = false;
	Wait(); // Merge
}

bool GameServer::IsRunning()
{
	return isRunning;
}

int GameServer::GetNumClients()
{
	return numClients;
}

int GameServer::GetGlobalChangeQueueLength()
{
	return (int)changes.size();
}

int GameServer::GetNextChangeNumber()
{
	return (int)nextChangeNumber;
}

int GameServer::GetWorldTime()
{
	return gameworld.worldTime;
}

void GameServer::Run()
{
	nextChangeNumber = 1; // Starting from scratch (again?)
	PopulateWorld();

	float lastPhysicsUpdate = timer.GetElapsedTime();
	sf::Uint32 lastConfirmedTS = 0;
	sf::Uint16 maximumSilenceDelay = 4; //Number of packets to allow uncorfimed world simulation when no changes are occuring
	while (isRunning)
	{
		ProcessPacketQueue();
		while (lastPhysicsUpdate + TIME_STEP < timer.GetElapsedTime())
		{
			lastPhysicsUpdate += TIME_STEP;
			gameworld.Step();

			avgNumPackets = avgNumPackets * 0.99f + (numPacketsReceived - lastNumPacketsReceived) * 0.01f;
			lastNumPacketsReceived = numPacketsReceived;
		}

		sf::Uint32 oldestRequiredChange = nextChangeNumber;

		// Sync with client
		for (map<sf::Uint32, Player*>::iterator client = clients.begin(); client != clients.end(); ++client)
		{
			UpdateClient(*(client->second));

			// Collect id of oldest required change
			if (client->second->state == Player::PLAYING && client->second->confirmedChangeNumber < oldestRequiredChange )
			{
				oldestRequiredChange = client->second->confirmedChangeNumber;
			}
			else if (client->second->state != Player::PLAYING && client->second->changeNumberWhenConnected < oldestRequiredChange )
			{
				oldestRequiredChange = client->second->changeNumberWhenConnected;
			}
		}

		// if no changes have occured in some time, inform clients that their confirmed simulation can be stepped forwarded.
		if (clients.size() > 0)
		{
			if (changes.size() > 0)
			{
				Change& lastChange = *changes.back();
				if ( lastChange.timestamp > lastConfirmedTS)
				{
					lastConfirmedTS = lastChange.timestamp;
				}
			}
			if (gameworld.worldTime - lastConfirmedTS > maximumSilenceDelay)
			{
				changes.push_back(new Change(gameworld.worldTime, nextChangeNumber));
				++nextChangeNumber;
			}
		}


		// Delete old changes.
		std::list<Change*>::iterator changeIT = changes.begin();
		while (changeIT != changes.end() && (**changeIT).number < oldestRequiredChange)
		{
			delete *changeIT;
			changeIT = changes.erase(changeIT);
		}

#ifdef WIN32
		Sleep(50);
#else
		usleep(50000);
#endif
	}
}

void GameServer::UpdateClient(Player& client)
{
	switch (client.state)
	{
		case Player::CONNECTING:
			{
				// Construct reply to client.
				sf::Packet reply = CreatePacketHeader(client.id, MESSAGE::TYPE::CONNECT)
					<< MESSAGE::CONNECT::CONNECT << client.connectionTime;
				network.Send(reply, client.address, client.port);
				// TODO: Kill timeout'ed clients
			}
			break;

		case Player::SYNCHRONIZING:
			if (client.changes.size() > 0)
			{
				sf::Packet packet = CreatePacketHeader(client.id, MESSAGE::TYPE::CONNECT)
					<< MESSAGE::CONNECT::SYNCRONIZING;
				for (std::list<Change*>::iterator changeIT = client.changes.begin(); changeIT != client.changes.end(); ++changeIT)
				{
					packet << **changeIT;
				}
				network.Send(packet, client.address, client.port);
			}
			break;

		case Player::SYNCHRONIZED:
			{
				sf::Packet packet = CreatePacketHeader(client.id, MESSAGE::TYPE::CONNECT)
					<< MESSAGE::CONNECT::SYNCRONIZED << client.shipID << client.confirmedChangeNumber;
				network.Send(packet, client.address, client.port);
			}
			break;

		case Player::PLAYING:
			{
				// Send delta
				sf::Packet packet = CreatePacketHeader(client.id, MESSAGE::TYPE::WORLD_UPDATE);
				std::list<Change*>::iterator changeIT = changes.begin();
				while (changeIT != changes.end() && (**changeIT).number <= client.confirmedChangeNumber)
					++changeIT;
				const int MAX_NUM_CHANGES = 5;
				int count = 0;
				while (changeIT != changes.end() && count < MAX_NUM_CHANGES)
				{
					packet << **changeIT;
					++count;
					++changeIT;
				}
				network.Send(packet, client.address, client.port);
			}
			break;
	}
//	if (client->justConnected)
//	{
//		sf::Uint32 timestamp = client->confirmedTime;
//		int numPackets = 1;
//		while (numPackets > 0 && client->changes->begin() > client->changes->end())
//		{
//
//		}
//
//
//	}
//
//	if (client->justConnected)
//	{
//		client-A
//		GameObjectMap objects = gameworld.GetAllObjects();
//		sf::Packet worldPacket = CreateNewPacket(
//			client->id,
//			MESSAGE::TYPE::WORLD_UPDATE,
//			MESSAGE::WORLD_UPDATE::CREATE_OBJ)
//			<< gameworld.worldTime;
//
//		for (GameObjectMapIT objIT = objects.begin(); objIT != objects.end(); ++objIT)
//		{
//			sf::Vector2f pos = (objIT->second)->pos;
//			worldPacket << (*(objIT->second));
//		}
//		network.Send(worldPacket, client->address, client->port);
//		//client->justConnected = false; // Ofcourse we should wait for the player to confirm this over the network.
//	}
//	else
//	{
//		// Calcualte packet delta based on player last ack.
//	}
}

void GameServer::PopulateWorld()
{
	// Add old trusty planets.
	{
		sf::Uint32 objID = gameworld.CreateObject(OBJECT_TYPE::PLANET, sf::Vector2f(200, 400));
		GameObject* p = gameworld.GetObject(objID);
		p->size = 60; // Diameter
		p->mass = 60.f;
		p->angle = 0.0f;
		p->angVel = 0.1f;
	}
	{
		sf::Uint32 objID = gameworld.CreateObject(OBJECT_TYPE::PLANET, sf::Vector2f(550, 200));
		GameObject* p = gameworld.GetObject(objID);
		p->size = 90; // Diameter
		p->mass = 90.f;
		p->angle = 0.0f;
		p->angVel = 0.09f;
	}
	{
		sf::Uint32 objID = gameworld.CreateObject(OBJECT_TYPE::PLANET, sf::Vector2f(950,700));
		GameObject* p = gameworld.GetObject(objID);
		p->size = 150; // Diameter
		p->mass = 150;
		p->angle = 10.0f;
		p->angVel = 0.09f;
	}

	// Some test weapons
	for (int i = 0; i < 5; ++i)
	{
		sf::Uint32 objID = gameworld.CreateObject(OBJECT_TYPE::WEAPON, sf::Vector2f(950 + cos(i * M_PI / 3) * 300,700  + sin(i * M_PI / 3) * 300));
		GameObject* p = gameworld.GetObject(objID);
		p->size = 10; // Diameter
		p->mass = 5;
		p->angle = 10.0f;
		p->angVel = 1.5f;
		p->vel.x = p->vel.y = 0.0f;
	}
}

void GameServer::ProcessPacketQueue()
{
	std::queue<PacketWithSource*>* packages = network.GetReadyPackages();
	if (packages == NULL)
	{
		return;
	}

	while (!packages->empty())
	{
		PacketWithSource* pac = packages->front();
		packages->pop();
		HandlePacket(pac->sender, pac->port, pac->packet);
		delete pac;
	}
}

// Packet parser imlementation
void GameServer::HandleAdminPacket(
	sf::IPAddress sender,
	unsigned short port,
	sf::Uint32 clientID,
	sf::Packet& packet)
{
	//std::cout << "Error, Admin packet handling not imlemented yet!";
	stringstream ss;
	ss << "Rx Admin packet From " << clientID;
	LogManager::Log("gameserver", ss.str());

}

void GameServer::HandleConnectionPacket(
	sf::IPAddress sender,
	unsigned short port,
	sf::Uint32 clientID,
	sf::Packet& packet)
{
	sf::Uint8 type;
	packet >> type;

	stringstream ss;
	ss << "Rx Connection packet from " << clientID << " Type: " << (int)type ;
	LogManager::Log("gameserver", ss.str());

	switch (type)
	{
		case MESSAGE::CONNECT::CONNECT:
		{
			//std::cout << "Recieved connection attempt: " << std::endl;

			// Rest of packet shopud be player name
			string playerName;
			packet >> playerName;
			//std::cout << "Player alias: " << playerName << std::endl;

			Player* player = NULL;
			// For some reason, the clients already knows his ID and is connecting with that.
			if (clientID == 0)
			{
			}

			// Verify that the same client is not requesting connection
			for (std::map<sf::Uint32, Player*>::iterator clIT = clients.begin(); (player == NULL) && (clIT != clients.end()); clIT++)
			{
				Player* candiate = clIT->second;
				if (candiate->address == sender && candiate->port == port && candiate->alias == playerName)
				{
					player = candiate;
				}
			}

			// If we didn't find a player from this IP +etc, create a new player.,
			if (player == NULL)
			{
			// A new client, without an id
				// construct player object and Add to client list.
				player = new Player(nextClientID, playerName, sender, port);
				//std::cout << "Creating new client: " << playerName << " " << sender << " " << port << std::endl;
				++nextClientID;
				clients[player->id] = player;
				++numClients;
				player->shipID = 0;

				player->changeNumberWhenConnected = nextChangeNumber;
				player->connectionTime = gameworld.worldTime; // TODO, update diagram

				// Get state of world and add as changes
				GameObjectMap objects = gameworld.GetAllObjects();
				sf::Uint32 nextClientChangeNumber = 1;
				for (GameObjectMapIT object = objects.begin(); object != objects.end(); ++object)
				{
					player->changes.push_back(new Change(gameworld.worldTime, nextClientChangeNumber, *object->second));
					++nextClientChangeNumber;
				}
			}

			// PLayer already in connection queue with changes etc. don't think any handling is necessary.
			else
			{
			}
		}
		break;

		case MESSAGE::CONNECT::SYNCRONIZING:
			if (clients.find(clientID) != clients.end())
			{
				Player& client = *clients[clientID];
				if (client.state != Player::SYNCHRONIZED)
				{
					client.state = Player::SYNCHRONIZING;
					cout << "SYNCHRONIZING" << endl;
					sf::Uint32 confirmedChangeNumber;
					packet >> confirmedChangeNumber;

					// Delete all confirmed changes
					while (client.changes.size() > 0 && (**client.changes.begin()).number <= confirmedChangeNumber)
					{
						delete *client.changes.begin();
						client.changes.erase(client.changes.begin());
					}

					// TODO: Handle wrap-around problem
					if (confirmedChangeNumber > client.confirmedChangeNumber)
					{
						client.confirmedChangeNumber = confirmedChangeNumber;
					}

					// Nothing more to send, start sending sync'ed msg's
					if (client.changes.size() == 0)
					{
						// Switching player to synchronized and give him ship ID
						client.state = Player::SYNCHRONIZED;
						cout << "SYNCHRONIZED" << endl;

						// Make a ship for the player
						if (client.shipID == 0)
						{
							client.confirmedChangeNumber = client.changeNumberWhenConnected - 1;
							client.shipID = gameworld.CreateShip( sf::Vector2f(100,100) );
						}
					}
				}
				else
				{
					packet.Clear();
				}
			}
			else
			{
				packet.Clear();
			}
			break;

		case MESSAGE::CONNECT::SYNCRONIZED:
			// Player's local change-queue empty, meaning world is synchronized... lets start PLAYING baby!
			if (clients.find(clientID) != clients.end())
			{
				Player& client = *clients[clientID];

				// Ignore repeating packets after receiving first ack
				if (client.state == Player::SYNCHRONIZED)
				{
					client.state = Player::PLAYING;
					cout << "PLAYING" << endl;
					client.confirmedChangeNumber = client.changeNumberWhenConnected - 1;
				}
			}
			break;

		case MESSAGE::CONNECT::DISCONNECT:
			//std::cout << "Client " << clientID << " disconnecting" << endl;
			for (map<sf::Uint32, Player*>::iterator client = clients.begin(); client != clients.end(); ++client)
			{
				if (client->second->id == clientID)
				{
					if ( !gameworld.DeleteObject(client->second->shipID) )
					{
						//std::cout << "Error deleting clients ship!" << endl;
					}
					delete client->second;
					clients.erase(client);

					--numClients;

					return;
				}
			}
			break;

		default:
			{
				std::stringstream ss;
				ss << "Received connection packet from clientID " << clientID << " of unknown type. Clearly a bug (or spam). Packet body: " << PacketToString(packet);
				LogManager::Log("gameserver", ss.str());
			}
			break;
	}
}

void GameServer::HandleWorldUpdatePacket(
	sf::IPAddress sender,
	unsigned short port,
	sf::Uint32 clientID,
	sf::Packet& packet)
{
	// The client must control some ship in the world
	if (clients.find(clientID) != clients.end())
	{
		Player& client = *clients[clientID];

		//stringstream ss;
		//ss << "Rx World update From " << client.id << ", ship: " << client.shipID << ", confirms: " << client.confirmedChangeNumber;
		//LogManager::Log("gameserver", ss.str());

		// Apply change on player's ship and queue change for everyone
		GameObject* playerShip = gameworld.GetObject(client.shipID);
		sf::Uint32 confirmedChangeNumber;
		sf::Uint8 newControl;
		packet >> confirmedChangeNumber >> newControl;

		// If client not in playing state yet, then discard all game events
		if (playerShip != NULL && client.state == Player::PLAYING && client.confirmedChangeNumber <= confirmedChangeNumber)
		{
			client.confirmedChangeNumber = confirmedChangeNumber;
			if (playerShip->controls != newControl)
			{
				gameworld.UpdateShipControls(*playerShip, newControl);
			}
		}
	}
	else
	{
		packet.Clear();
	}
}

void GameServer::ObjectCreated(const GameObject& object)
{
	stringstream ss;
	ss << "Object Created: " << object.id;
	LogManager::Log("gameserver", ss.str());

	changes.push_back(new Change(gameworld.worldTime, nextChangeNumber, object));
	++nextChangeNumber;
}

void GameServer::ObjectDeleted(const GameObject& object)
{
	stringstream ss;
	ss << "Object Deleted: " << object.id;
	LogManager::Log("gameserver", ss.str());

	changes.push_back(new Change(gameworld.worldTime, nextChangeNumber, object.id));
	++nextChangeNumber;
}

void GameServer::ShipKilled(const GameObject& object)
{
	stringstream ss;
	ss << "Ship killed: " << object.id;
	LogManager::Log("gameserver", ss.str());
}

void GameServer::ShipControlChanged(const GameObject& object)
{
	stringstream ss;
	ss << "Ship Control changed: " << object.id;

	changes.push_back(new Change(gameworld.worldTime, nextChangeNumber, object.id, object.controls));
	++nextChangeNumber;
}
